
package com.hulk.summary.wrapper;


import cn.hutool.crypto.digest.*;
import com.hulk.summary.SummaryProperties;
import com.hulk.summary.enums.SummaryType;
import com.hulk.summary.exception.SummaryException;
import com.hulk.summary.util.JacksonUtils;
import com.hulk.summary.util.RequestUtils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.*;


/**
 * Request包装类
 * <p>
 * 2.拓展requestbody无限获取(HttpServletRequestWrapper只能获取一次)
 * </p>
 *
 * @author hulk
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    /**
     * 存储requestBody byte[]
     */
    private final byte[] body;

    private List<String> excludeFields =new ArrayList<>();

    private SummaryProperties properties ;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        this.body = RequestUtils.getByteBody(request);
    }
    public RequestWrapper(HttpServletRequest request,List<String> excludeFields) {
        super(request);
        this.body = RequestUtils.getByteBody(request);
        this.excludeFields.addAll(excludeFields);
    }

    public RequestWrapper(HttpServletRequest request,List<String> excludeFields,SummaryProperties p) {
        super(request);
        this.body = RequestUtils.getByteBody(request);
        this.excludeFields.addAll(excludeFields);
        this. properties=p;
    }
    @Override
    public BufferedReader getReader() {
        return ObjectUtils.isEmpty(body) ? null : new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return getServletInputStream(bais);
    }

    /**
     * 获取ServletInputStream
     *
     * @param bais
     * @return
     */
    private ServletInputStream getServletInputStream(ByteArrayInputStream bais) {
        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            @SuppressWarnings("EmptyMethod")
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                return bais.read();
            }
        };
    }

    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = handler(values[i]);
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null) {
            return null;
        }
        return handler(value);
    }

    @Override
    public Object getAttribute(String name) {
        Object value = super.getAttribute(name);
        if (value instanceof String) {
            handler((String) value);
        }
        return value;
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null) {
            return null;
        }
        return handler(value);
    }

    @Override
    public String getQueryString() {
        String value = super.getQueryString();
        if (value == null) {
            return null;
        }
        return handler(value);
    }

    /**
     * 使用spring HtmlUtils 转义html标签达到预防xss攻击效果
     *
     * @param str
     * @see org.springframework.web.util.HtmlUtils#htmlEscape
     */
    protected String handler(String str) {


        Map<String,Object> mmap = JacksonUtils.readValue(str, HashMap.class);
        Iterator<Map.Entry<String,Object>> it = mmap.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<String,Object> entry = it.next();
            String key = entry.getKey();
            if ((!excludeFields.isEmpty()) && excludeFields.contains(key) ){
                it.remove();
            }
        }

        String signature = String.valueOf(mmap.get(properties.getSummaryName()));

        SummaryType  summaryType= properties.getSummaryType();


        String signStr = createSignStr( new TreeMap<>(mmap),  properties);

        if(!checkSign(signStr,signature,summaryType)){
            throw new SummaryException("签名验证不通过");
        }
        //return HtmlUtils.htmlEscape(str);
        return str;
    }

    private static boolean  checkSign(String plainText , String signature , SummaryType summaryType) {

        if ("DigestAlgorithm".equals(summaryType.getType())) {

            Digester digester = new Digester(summaryType.getValue());
            String digestHex = digester.digestHex(plainText);
            if (Objects.equals(digestHex, signature)) {
                return true;
            }
            return false;

        }
/*        else if ("HmacAlgorithm".equals(summaryType.getType())) {

            HMac mac = new HMac(summaryType.getValue(),"".getBytes(StandardCharsets.UTF_8));

            String macHex1 = mac.digestHex(testStr);
            if (Objects.equals(digestHex, signature)) {
                return true;
            }
            return false;

        }*/
        else if ("SM".equals(summaryType.getType())) {
            Digester digester = DigestUtil.digester(summaryType.getType());
            String digestHex = digester.digestHex(plainText);
            if (Objects.equals(digestHex, signature)) {
                return true;
            }
            return false;
        } else {
            throw new SummaryException("未匹配到签名算法");
        }
    }






    /**
     * 获取签名字符串
     * @param parameters 签名Map
     * @return 签名字符串
     */

    //TODO 如果json中包含list需要测试
    private static String createSignStr(SortedMap<String, Object> parameters, SummaryProperties p) {
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String,Object>> es = parameters.entrySet();
        for (Map.Entry<String,Object> e : es) {
            String k =  e.getKey();
            String v = (e.getValue() == null ||
                    e.getValue() instanceof List ||
                    e.getValue().getClass().isArray()
            ) ? "" : e.getValue().toString();
            //空值不传递，不参与签名组串
            if (null != v && !"".equals(v) && !Objects.equals(k,p.getSummaryName())) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
       log.debug(sb.toString());
       String name =  String.valueOf(parameters.get(p.getName()));
       String mval =  p.getSafes().get(name);
        //排序后的字符串
        return sb.append(mval).toString();
    }

}